package GUITools;

import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.LayoutManager;

/**
 * 
 * @author Brandon James Talbot (TLBBRA002)
 * 
 *         This is a modified Grid layout. This grid layout allows for resizing
 *         correctly of the frame so that scroll panes work correctly. It has an
 *         infinite vertical length. It also sizes each component correctly
 */

public class GridLayoutVerticalMod implements LayoutManager,
		java.io.Serializable {

	/**
	 * Serial Version UID (Generated by Eclipse)
	 */
	private static final long serialVersionUID = 805335974745806949L;
	/**
	 * Horizontal gap parameter (holds the gap size set by user)
	 */
	int HorizGap;
	/**
	 * Vertical gap parameter (holds the gap size set by user)
	 */
	int VertGap;
	/**
	 * Holds the number of columns the grid must contain
	 */
	int Columns;

	/**
	 * Default Constructor Takes in the number of columns the grid must go to
	 * Sets the Horizontal and Vertical gaps to the default 5 units
	 * 
	 * @param columns
	 *            Number of columns for the grid
	 */
	public GridLayoutVerticalMod(int columns) {
		this(columns, 5, 5);
	}

	/**
	 * Constructor takes in the number of columns the grid must have Takes in
	 * the Horizontal and Vertical gaps
	 * 
	 * @param columns
	 *            number of columns for the grid
	 * @param HorizontalGap
	 *            The size for the horizontal gaps
	 * @param VerticalGap
	 *            The size for the Vertical gaps
	 */
	public GridLayoutVerticalMod(int columns, int HorizontalGap, int VerticalGap) {
		this.Columns = columns;
		this.HorizGap = HorizontalGap;
		this.VertGap = VerticalGap;
	}

	// / ---------------------------------------------------------------------
	// getters and setters
	/**
	 * Returns the chosen Horizontal gap size chosen
	 * 
	 * @return Horizontal gap size
	 */
	public int getHorizGap() {
		return HorizGap;
	}

	/**
	 * Sets the value for the horizontal gap size.
	 * 
	 * @param horizGap
	 *            the new gap size
	 */
	public void setHorizGap(int horizGap) {
		HorizGap = horizGap;
	}

	/**
	 * Gets the Vertical gap size
	 * 
	 * @return The vertical gap size
	 */
	public int getVertGap() {
		return VertGap;
	}

	/**
	 * Set the vertical gap size
	 * 
	 * @param vertGap
	 *            The vertical gap size
	 */
	public void setVertGap(int vertGap) {
		VertGap = vertGap;
	}

	/**
	 * Gets the number of columns used for the Grid
	 * 
	 * @return The number of columns
	 */
	public int getColumns() {
		return Columns;
	}

	/**
	 * Sets the columns for the grid
	 * 
	 * @param columns
	 *            The number of columns
	 */
	public void setColumns(int columns) {
		Columns = columns;
	}

	// ---------------------------------------------------------------------------

	/**
	 * Determines the minimum size for the container
	 * 
	 * @param target
	 *            The container to determine the minimum size for
	 * @return The minimum size for the container as Dimension
	 */
	public Dimension minimumLayoutSize(Container target) {
		synchronized (target.getTreeLock()) {
			Dimension dim = new Dimension(0, 0);

			int count = 0;
			int num = target.getComponentCount();

			while (count < num) {
				int numComponents = Math.min(Columns, num - count);

				int h = genHeight(target, count, numComponents, false);
				int w = genWidth(target, count, numComponents, false);

				dim.width = Math.max(w, dim.width);
				dim.height += h;

				count += numComponents;
			}

			Insets in = target.getInsets();

			dim.width += in.left + in.right + HorizGap;
			dim.height += in.top + in.bottom + VertGap;

			return dim;
		}
	}

	/**
	 * Determines the preferred size for the container specified
	 * 
	 * @param target
	 *            the target container
	 * @return The dimension for the preferred size
	 */
	public Dimension preferredLayoutSize(Container target) {
		synchronized (target.getTreeLock()) {
			Dimension dim = new Dimension(0, 0);

			int count = 0;
			int num = target.getComponentCount();

			while (count < num) {
				int numComponents = Math.min(Columns, num - count);

				int h = genHeight(target, count, numComponents, true);
				int w = genWidth(target, count, numComponents, true);

				dim.width = Math.max(w, dim.width);
				dim.height += h;

				count += numComponents;
			}

			Insets in = target.getInsets();

			dim.width += in.left + in.right + HorizGap;
			dim.height += in.top + in.bottom + VertGap;

			return dim;
		}
	}

	/**
	 * Generates the height for the set of values
	 * 
	 * @param target
	 *            The target container to use
	 * @param start
	 *            The starting point for the components
	 * @param num
	 *            The number of components to look at
	 * @param pref
	 *            Whether it is the prefered size or min size
	 * @return The height value for the components
	 */
	public int genHeight(Container target, int start, int num, boolean pref) {
		int height = 0;

		for (int i = start; i < start + num; ++i) {
			Component com = target.getComponent(i);
			height = Math.max(height, pref ? com.getPreferredSize().height
					: com.getMinimumSize().height);
		}

		height += VertGap;

		return height;
	}

	/**
	 * Generates the width value for the specified components
	 * 
	 * @param target
	 *            The target container to look at
	 * @param start
	 *            The starting component to look at
	 * @param num
	 *            The number of components to look at
	 * @param pref
	 *            Whether it is preffered or mininmum size
	 * @return The width value
	 */
	public int genWidth(Container target, int start, int num, boolean pref) {
		int width = 0;

		for (int i = start; i < start + num; ++i) {
			Component com = target.getComponent(i);

			width += (pref ? com.getPreferredSize().width : com
					.getMinimumSize().width) + HorizGap;
		}

		return width;
	}

	/**
	 * Sets the size and positions of the containers for the layout to look
	 * correctly.
	 * 
	 * @param target
	 *            The target container
	 */
	public void layoutContainer(Container target) {
		synchronized (target.getTreeLock()) {
			int count = 0;
			int numCom = target.getComponentCount();

			int y = target.getInsets().top + VertGap;

			while (count < numCom) {
				int numComponents = Math.min(Columns, numCom - count);

				int max = setObjects(target, count, numComponents, y);

				y += max + VertGap;

				count += numComponents;
			}

		}
	}

	/**
	 * Sets the set of objects positions and size for the current row
	 * 
	 * @param target
	 *            The target container to layout
	 * @param start
	 *            The starting component
	 * @param numCom
	 *            The number of components in the row
	 * @param y
	 *            The y position to place the components
	 * @return The height value for this container
	 */
	public int setObjects(Container target, int start, int numCom, int y) {
		Insets insets = target.getInsets();

		int usableWidth = target.getSize().width - HorizGap * (numCom + 1)
				- insets.left - insets.right;
		int x = insets.left + HorizGap;

		int widthPer = usableWidth / numCom;

		int max = Integer.MIN_VALUE;

		for (int i = start; i < numCom + start; ++i) {
			Component c = target.getComponent(i);

			c.setSize(widthPer, c.getPreferredSize().height);

			max = Math.max(max, c.getPreferredSize().height);

			c.setLocation(x, y);

			x += widthPer + HorizGap;
		}

		return max;
	}

	/**
	 * Not used here
	 */
	public void addLayoutComponent(String arg0, Component arg1) {
	}

	/**
	 * Not used here
	 */
	public void removeLayoutComponent(Component arg0) {
	}

}
